1   /*
2    * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    *   - Redistributions of source code must retain the above copyright
9    *     notice, this list of conditions and the following disclaimer.
10   *
11   *   - Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in the
13   *     documentation and/or other materials provided with the distribution.
14   *
15   *   - Neither the name of Oracle nor the names of its
16   *     contributors may be used to endorse or promote products derived
17   *     from this software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  package j2dbench.tests;
33  
34  import j2dbench.Destinations;
35  import j2dbench.Group;
36  import j2dbench.Modifier;
37  import j2dbench.Option;
38  import j2dbench.TestEnvironment;
39  import java.awt.Graphics;
40  import java.awt.Graphics2D;
41  import java.awt.Color;
42  import java.awt.Image;
43  import java.awt.Canvas;
44  import java.awt.AlphaComposite;
45  import java.awt.Dimension;
46  import java.awt.GraphicsConfiguration;
47  import java.awt.image.BufferedImage;
48  import java.awt.image.BufferedImageOp;
49  import java.awt.image.ByteLookupTable;
50  import java.awt.image.ConvolveOp;
51  import java.awt.image.DataBuffer;
52  import java.awt.image.IndexColorModel;
53  import java.awt.image.Kernel;
54  import java.awt.image.LookupOp;
55  import java.awt.image.Raster;
56  import java.awt.image.RasterOp;
57  import java.awt.image.RescaleOp;
58  import java.awt.image.ShortLookupTable;
59  import java.awt.image.VolatileImage;
60  import java.awt.image.WritableRaster;
61  import java.awt.Transparency;
62  import java.awt.geom.AffineTransform;
63  import java.awt.image.DataBufferByte;
64  import java.awt.image.DataBufferInt;
65  import java.awt.image.DataBufferShort;
66  import java.util.ArrayList;
67  import javax.swing.JComponent;
68  
69  public abstract class ImageTests extends GraphicsTests {
70      public static boolean hasVolatileImage;
71      public static boolean hasCompatImage;
72  
73      static {
74          try {
75              hasVolatileImage = (VolatileImage.class != null);
76          } catch (NoClassDefFoundError e) {
77          }
78          try {
79              new Canvas().getGraphicsConfiguration();
80              hasCompatImage = true;
81          } catch (NoSuchMethodError e) {
82          }
83      }
84  
85      static Group imageroot;
86      static Group.EnableSet imgsrcroot;
87      static Group.EnableSet bufimgsrcroot;
88  
89      static Group imgtestroot;
90      static Group imgoptionsroot;
91  
92      static Group imageOpRoot;
93      static Group imageOpOptRoot;
94      static Group imageOpTestRoot;
95      static Group graphicsTestRoot;
96      static Group bufImgOpTestRoot;
97      static Group rasterOpTestRoot;
98      static Option opList;
99      static Option doTouchSrc;
100 
101     static String transNodeNames[] = {
102         null, "opaque", "bitmask", "translucent",
103     };
104 
105     static String transDescriptions[] = {
106         null, "Opaque", "Bitmask", "Translucent",
107     };
108 
109     public static void init() {
110         imageroot = new Group(graphicsroot, "imaging",
111                               "Imaging Benchmarks");
112         imageroot.setTabbed();
113 
114         imgsrcroot = new Group.EnableSet(imageroot, "src",
115                                          "Image Rendering Sources");
116         imgsrcroot.setBordered(true);
117 
118         imgoptionsroot = new Group(imgsrcroot, "options",
119                                 "Image Source Options");
120         imgoptionsroot.setBordered(true);
121         doTouchSrc =
122             new Option.Toggle(imgoptionsroot, "touchsrc",
123                               "Touch src image before every operation",
124                                Option.Toggle.Off);
125 
126         imgtestroot = new Group(imageroot, "tests",
127                                 "Image Rendering Tests");
128         imgtestroot.setBordered(true);
129 
130         new OffScreen();
131 
132         if (hasGraphics2D) {
133             if (hasCompatImage) {
134                 new CompatImg(Transparency.OPAQUE);
135                 new CompatImg(Transparency.BITMASK);
136                 new CompatImg(Transparency.TRANSLUCENT);
137             }
138 
139             if (hasVolatileImage) {
140                 new VolatileImg();
141             }
142 
143             bufimgsrcroot =
144                 new Group.EnableSet(imgsrcroot, "bufimg",
145                                     "BufferedImage Rendering Sources");
146             new BufImg(BufferedImage.TYPE_INT_RGB);
147             new BufImg(BufferedImage.TYPE_INT_ARGB);
148             new BufImg(BufferedImage.TYPE_BYTE_GRAY);
149             new BufImg(BufferedImage.TYPE_3BYTE_BGR);
150             new BmByteIndexBufImg();
151             new BufImg(BufferedImage.TYPE_INT_RGB, true);
152             new BufImg(BufferedImage.TYPE_INT_ARGB, true);
153             new BufImg(BufferedImage.TYPE_3BYTE_BGR, true);
154 
155             imageOpRoot = new Group(imageroot, "imageops",
156                                     "Image Op Benchmarks");
157             imageOpOptRoot = new Group(imageOpRoot, "opts", "Options");
158             imageOpTestRoot = new Group(imageOpRoot, "tests", "Tests");
159             graphicsTestRoot = new Group(imageOpTestRoot, "graphics2d",
160                                          "Graphics2D Tests");
161             bufImgOpTestRoot = new Group(imageOpTestRoot, "bufimgop",
162                                          "BufferedImageOp Tests");
163             rasterOpTestRoot = new Group(imageOpTestRoot, "rasterop",
164                                          "RasterOp Tests");
165 
166             ArrayList opStrs = new ArrayList();
167             ArrayList opDescs = new ArrayList();
168             opStrs.add("convolve3x3zero");
169             opDescs.add("ConvolveOp (3x3 blur, zero)");
170             opStrs.add("convolve3x3noop");
171             opDescs.add("ConvolveOp (3x3 blur, noop)");
172             opStrs.add("convolve5x5zero");
173             opDescs.add("ConvolveOp (5x5 edge, zero)");
174             opStrs.add("convolve5x5noop");
175             opDescs.add("ConvolveOp (5x5 edge, noop)");
176             opStrs.add("lookup1byte");
177             opDescs.add("LookupOp (1 band, byte)");
178             opStrs.add("lookup1short");
179             opDescs.add("LookupOp (1 band, short)");
180             opStrs.add("lookup3byte");
181             opDescs.add("LookupOp (3 band, byte)");
182             opStrs.add("lookup3short");
183             opDescs.add("LookupOp (3 band, short)");
184             opStrs.add("rescale1band");
185             opDescs.add("RescaleOp (1 band)");
186             opStrs.add("rescale3band");
187             opDescs.add("RescaleOp (3 band)");
188             String[] opStrArr = new String[opStrs.size()];
189             opStrArr = (String[])opStrs.toArray(opStrArr);
190             String[] opDescArr = new String[opDescs.size()];
191             opDescArr = (String[])opDescs.toArray(opDescArr);
192             opList =
193                 new Option.ObjectList(imageOpOptRoot,
194                                       "op", "Operation",
195                                       opStrArr, opStrArr,
196                                       opStrArr, opDescArr,
197                                       0x1);
198             ((Option.ObjectList) opList).setNumRows(4);
199 
200             new DrawImageOp();
201             new BufImgOpFilter(false);
202             new BufImgOpFilter(true);
203             new RasterOpFilter(false);
204             new RasterOpFilter(true);
205         }
206 
207         new DrawImage();
208         new DrawImageBg();
209         new DrawImageScale("up", 1.5f);
210         new DrawImageScale("down", .75f);
211         new DrawImageTransform();
212     }
213 
214     public static class Context extends GraphicsTests.Context {
215         boolean touchSrc;
216         Image src;
217         AffineTransform tx;
218     }
219 
220     public ImageTests(Group parent, String nodeName, String description) {
221         this(parent, nodeName, description, null);
222     }
223 
224     public ImageTests(Group parent, String nodeName, String description,
225                       Modifier.Filter srcFilter)
226     {
227         super(parent, nodeName, description);
228         addDependency(imgsrcroot, srcFilter);
229         addDependency(doTouchSrc);
230     }
231 
232     public GraphicsTests.Context createContext() {
233         return new ImageTests.Context();
234     }
235 
236     public void initContext(TestEnvironment env, GraphicsTests.Context ctx) {
237         super.initContext(env, ctx);
238         ImageTests.Context ictx = (ImageTests.Context) ctx;
239 
240         ictx.src = env.getSrcImage();
241         ictx.touchSrc = env.isEnabled(doTouchSrc);
242     }
243 
244     public abstract static class TriStateImageType extends Group {
245         Image theImage;
246 
247         public TriStateImageType(Group parent, String nodename, String desc,
248                                  int transparency)
249         {
250             super(parent, nodename, desc);
251             setHorizontal();
252             new DrawableImage(this, Transparency.OPAQUE, true);
253             new DrawableImage(this, Transparency.BITMASK,
254                               (transparency != Transparency.OPAQUE));
255             new DrawableImage(this, Transparency.TRANSLUCENT,
256                               (transparency == Transparency.TRANSLUCENT));
257         }
258 
259         public Image getImage(TestEnvironment env, int w, int h) {
260             if (theImage == null ||
261                 theImage.getWidth(null) != w ||
262                 theImage.getHeight(null) != h)
263             {
264                 theImage = makeImage(env, w, h);
265             }
266             return theImage;
267         }
268 
269         public abstract Image makeImage(TestEnvironment env, int w, int h);
270     }
271 
272     public static class OffScreen extends TriStateImageType {
273         public OffScreen() {
274             super(imgsrcroot, "offscr", "Offscreen Image", Transparency.OPAQUE);
275         }
276 
277         public Image makeImage(TestEnvironment env, int w, int h) {
278             Canvas c = env.getCanvas();
279             return c.createImage(w, h);
280         }
281     }
282 
283     public static class VolatileImg extends TriStateImageType {
284         public VolatileImg() {
285             super(imgsrcroot, "volimg", "Volatile Image", Transparency.OPAQUE);
286         }
287 
288         public Image makeImage(TestEnvironment env, int w, int h) {
289             Canvas c = env.getCanvas();
290             return c.createVolatileImage(w, h);
291         }
292     }
293 
294     public static class CompatImg extends TriStateImageType {
295         int transparency;
296 
297         public CompatImg(int transparency) {
298             super(imgsrcroot,
299                   Destinations.CompatImg.ShortNames[transparency],
300                   Destinations.CompatImg.LongDescriptions[transparency],
301                   transparency);
302             this.transparency = transparency;
303         }
304 
305         public Image makeImage(TestEnvironment env, int w, int h) {
306             Canvas c = env.getCanvas();
307             GraphicsConfiguration gc = c.getGraphicsConfiguration();
308             return gc.createCompatibleImage(w, h, transparency);
309         }
310     }
311 
312     public static class BufImg extends TriStateImageType {
313         int type;
314         boolean unmanaged;
315 
316         static int Transparencies[] = {
317             Transparency.TRANSLUCENT, // "custom",
318             Transparency.OPAQUE,      // "IntXrgb",
319             Transparency.TRANSLUCENT, // "IntArgb",
320             Transparency.TRANSLUCENT, // "IntArgbPre",
321             Transparency.OPAQUE,      // "IntXbgr",
322             Transparency.OPAQUE,      // "3ByteBgr",
323             Transparency.TRANSLUCENT, // "4ByteAbgr",
324             Transparency.TRANSLUCENT, // "4ByteAbgrPre",
325             Transparency.OPAQUE,      // "Short565",
326             Transparency.OPAQUE,      // "Short555",
327             Transparency.OPAQUE,      // "ByteGray",
328             Transparency.OPAQUE,      // "ShortGray",
329             Transparency.OPAQUE,      // "ByteBinary",
330             Transparency.OPAQUE,      // "ByteIndexed",
331         };
332 
333         public BufImg(int type) {
334             this(type, false);
335         }
336 
337         public BufImg(int type, boolean unmanaged) {
338             super(bufimgsrcroot,
339                   (unmanaged ? "unmanaged" : "") +
340                   Destinations.BufImg.ShortNames[type],
341                   (unmanaged ? "Unmanaged " : "") +
342                   Destinations.BufImg.Descriptions[type],
343                   Transparencies[type]);
344             this.type = type;
345             this.unmanaged = unmanaged;
346         }
347 
348         public Image makeImage(TestEnvironment env, int w, int h) {
349             BufferedImage img = new BufferedImage(w, h, type);
350             if (unmanaged) {
351                 DataBuffer db = img.getRaster().getDataBuffer();
352                 if (db instanceof DataBufferInt) {
353                     ((DataBufferInt)db).getData();
354                 } else if (db instanceof DataBufferShort) {
355                     ((DataBufferShort)db).getData();
356                 } else if (db instanceof DataBufferByte) {
357                     ((DataBufferByte)db).getData();
358                 } else {
359                     try {
360                         img.setAccelerationPriority(0.0f);
361                     } catch (Throwable e) {}
362                 }
363             }
364             return img;
365         }
366     }
367 
368     public static class BmByteIndexBufImg extends TriStateImageType {
369         static IndexColorModel icm;
370 
371         public BmByteIndexBufImg() {
372             super(bufimgsrcroot,
373                   "ByteIndexedBm",
374                   "8-bit Transparent Indexed Image",
375                   Transparency.BITMASK);
376         }
377 
378         public Image makeImage(TestEnvironment env, int w, int h) {
379             if (icm == null) {
380                 int cmap[] = new int[256];
381                 // Workaround for transparency rendering bug in earlier VMs
382                 // Can only render transparency if first cmap entry is 0x0
383                 // This bug is fixed in 1.4.2 (Mantis)
384                 int i = 1;
385                 for (int r = 0; r < 256; r += 51) {
386                     for (int g = 0; g < 256; g += 51) {
387                         for (int b = 0; b < 256; b += 51) {
388                             cmap[i++] = (0xff<<24)|(r<<16)|(g<<8)|b;
389                         }
390                     }
391                 }
392 
393                 // Leave the rest of the colormap transparent
394 
395                 icm = new IndexColorModel(8, 256, cmap, 0, true, 255,
396                                           DataBuffer.TYPE_BYTE);
397             }
398             return new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED,
399                                      icm);
400         }
401     }
402 
403     public static class DrawableImage extends Option.Enable {
404         static Color transparentBlack  = makeAlphaColor(Color.black, 0);
405         static Color translucentRed    = makeAlphaColor(Color.red, 192);
406         static Color translucentGreen  = makeAlphaColor(Color.green, 128);
407         static Color translucentYellow = makeAlphaColor(Color.yellow, 64);
408 
409         static Color colorsets[][] = new Color[][] {
410             null,
411             {
412                 Color.blue,       Color.red,
413                 Color.green,      Color.yellow,
414                 Color.blue,
415             },
416             {
417                 transparentBlack, Color.red,
418                 Color.green,      transparentBlack,
419                 transparentBlack,
420             },
421             {
422                 Color.blue,       translucentRed,
423                 translucentGreen, translucentYellow,
424                 translucentRed,
425             },
426         };
427 
428         TriStateImageType tsit;
429         int transparency;
430         boolean possible;
431 
432         public DrawableImage(TriStateImageType parent, int transparency,
433                              boolean possible)
434         {
435             super(parent,
436                   transNodeNames[transparency],
437                   transDescriptions[transparency],
438                   false);
439             this.tsit = parent;
440             this.transparency = transparency;
441             this.possible = possible;
442         }
443 
444         public int getTransparency() {
445             return transparency;
446         }
447 
448         public JComponent getJComponent() {
449             JComponent comp = super.getJComponent();
450             comp.setEnabled(possible);
451             return comp;
452         }
453 
454         public String setValueFromString(String value) {
455             if (!possible && !value.equalsIgnoreCase("disabled")) {
456                 return "Bad Value";
457             }
458             return super.setValueFromString(value);
459         }
460 
461         public void modifyTest(TestEnvironment env) {
462             int size = env.getIntValue(sizeList);
463             Image src = tsit.getImage(env, size, size);
464             Graphics g = src.getGraphics();
465             if (hasGraphics2D) {
466                 ((Graphics2D) g).setComposite(AlphaComposite.Src);
467             }
468             if (size == 1) {
469                 g.setColor(colorsets[transparency][4]);
470                 g.fillRect(0, 0, 1, 1);
471             } else {
472                 int mid = size/2;
473                 g.setColor(colorsets[transparency][0]);
474                 g.fillRect(0, 0, mid, mid);
475                 g.setColor(colorsets[transparency][1]);
476                 g.fillRect(mid, 0, size-mid, mid);
477                 g.setColor(colorsets[transparency][2]);
478                 g.fillRect(0, mid, mid, size-mid);
479                 g.setColor(colorsets[transparency][3]);
480                 g.fillRect(mid, mid, size-mid, size-mid);
481             }
482             g.dispose();
483             env.setSrcImage(src);
484         }
485 
486         public void restoreTest(TestEnvironment env) {
487             env.setSrcImage(null);
488         }
489 
490         public String getAbbreviatedModifierDescription(Object value) {
491             return "from "+getModifierValueName(value);
492         }
493 
494         public String getModifierValueName(Object val) {
495             return getParent().getNodeName()+" "+getNodeName();
496         }
497     }
498 
499     public static class DrawImage extends ImageTests {
500         public DrawImage() {
501             super(imgtestroot, "drawimage", "drawImage(img, x, y, obs);");
502         }
503 
504         public void runTest(Object ctx, int numReps) {
505             ImageTests.Context ictx = (ImageTests.Context) ctx;
506             int x = ictx.initX;
507             int y = ictx.initY;
508             Graphics g = ictx.graphics;
509             g.translate(ictx.orgX, ictx.orgY);
510             Image src = ictx.src;
511             if (ictx.animate) {
512                 if (ictx.touchSrc) {
513                     Graphics srcG = src.getGraphics();
514                     do {
515                         srcG.fillRect(0, 0, 1, 1);
516                         g.drawImage(src, x, y, null);
517                         if ((x -= 3) < 0) x += ictx.maxX;
518                         if ((y -= 1) < 0) y += ictx.maxY;
519                     } while (--numReps > 0);
520                 } else {
521                     do {
522                         g.drawImage(src, x, y, null);
523                         if ((x -= 3) < 0) x += ictx.maxX;
524                         if ((y -= 1) < 0) y += ictx.maxY;
525                     } while (--numReps > 0);
526                 }
527             } else {
528                 if (ictx.touchSrc) {
529                     Graphics srcG = src.getGraphics();
530                     do {
531                         srcG.fillRect(0, 0, 1, 1);
532                         g.drawImage(src, x, y, null);
533                     } while (--numReps > 0);
534                 } else {
535                     do {
536                         g.drawImage(src, x, y, null);
537                     } while (--numReps > 0);
538                 }
539             }
540             g.translate(-ictx.orgX, -ictx.orgY);
541         }
542     }
543 
544     public static class DrawImageBg extends ImageTests {
545         public DrawImageBg() {
546             super(imgtestroot, "drawimagebg", "drawImage(img, x, y, bg, obs);",
547                   new Modifier.Filter() {
548                       public boolean isCompatible(Object val) {
549                           DrawableImage di = (DrawableImage) val;
550                           return (di.getTransparency() != Transparency.OPAQUE);
551                       }
552                   });
553         }
554 
555         public void runTest(Object ctx, int numReps) {
556             ImageTests.Context ictx = (ImageTests.Context) ctx;
557             int x = ictx.initX;
558             int y = ictx.initY;
559             Graphics g = ictx.graphics;
560             g.translate(ictx.orgX, ictx.orgY);
561             Image src = ictx.src;
562             Color bg = Color.orange;
563             if (ictx.animate) {
564                 if (ictx.touchSrc) {
565                     Graphics srcG = src.getGraphics();
566                     do {
567                         srcG.fillRect(0, 0, 1, 1);
568                         g.drawImage(src, x, y, bg, null);
569                         if ((x -= 3) < 0) x += ictx.maxX;
570                         if ((y -= 1) < 0) y += ictx.maxY;
571                     } while (--numReps > 0);
572                 } else {
573                     do {
574                         g.drawImage(src, x, y, bg, null);
575                         if ((x -= 3) < 0) x += ictx.maxX;
576                         if ((y -= 1) < 0) y += ictx.maxY;
577                     } while (--numReps > 0);
578                 }
579             } else {
580                 if (ictx.touchSrc) {
581                     Graphics srcG = src.getGraphics();
582                     do {
583                         srcG.fillRect(0, 0, 1, 1);
584                         g.drawImage(src, x, y, bg, null);
585                     } while (--numReps > 0);
586                 } else {
587                     do {
588                         g.drawImage(src, x, y, bg, null);
589                     } while (--numReps > 0);
590                 }
591             }
592             g.translate(-ictx.orgX, -ictx.orgY);
593         }
594     }
595 
596     public static class DrawImageScale extends ImageTests {
597         float scale;
598 
599         public DrawImageScale(String dir, float scale) {
600             super(imgtestroot, "drawimagescale"+dir,
601                                "drawImage(img, x, y, w*"+scale+", h*"+scale+", obs);");
602             this.scale = scale;
603         }
604 
605         public Dimension getOutputSize(int w, int h) {
606             int neww = (int) (w * scale);
607             int newh = (int) (h * scale);
608             if (neww == w && scale > 1f) neww = w+1;
609             if (newh == h && scale > 1f) newh = h+1;
610             return new Dimension(neww, newh);
611         }
612 
613         public void runTest(Object ctx, int numReps) {
614             ImageTests.Context ictx = (ImageTests.Context) ctx;
615             int x = ictx.initX;
616             int y = ictx.initY;
617             int w = ictx.outdim.width;
618             int h = ictx.outdim.height;
619             Graphics g = ictx.graphics;
620             g.translate(ictx.orgX, ictx.orgY);
621             Image src = ictx.src;
622             if (ictx.animate) {
623                 if (ictx.touchSrc) {
624                     Graphics srcG = src.getGraphics();
625                     do {
626                         srcG.fillRect(0, 0, 1, 1);
627                         g.drawImage(src, x, y, w, h, null);
628                         if ((x -= 3) < 0) x += ictx.maxX;
629                         if ((y -= 1) < 0) y += ictx.maxY;
630                     } while (--numReps > 0);
631                 } else {
632                     do {
633                         g.drawImage(src, x, y, w, h, null);
634                         if ((x -= 3) < 0) x += ictx.maxX;
635                         if ((y -= 1) < 0) y += ictx.maxY;
636                     } while (--numReps > 0);
637                 }
638             } else {
639                 Graphics srcG = src.getGraphics();
640                 if (ictx.touchSrc) {
641                     do {
642                         srcG.fillRect(0, 0, 1, 1);
643                         g.drawImage(src, x, y, w, h, null);
644                     } while (--numReps > 0);
645                 } else {
646                     do {
647                         g.drawImage(src, x, y, w, h, null);
648                     } while (--numReps > 0);
649                 }
650             }
651             g.translate(-ictx.orgX, -ictx.orgY);
652         }
653     }
654 
655     public static class DrawImageTransform extends ImageTests {
656         public DrawImageTransform() {
657             super(imgtestroot, "drawimagetxform", "drawImage(img, tx, obs);");
658         }
659 
660         public Dimension getOutputSize(int w, int h) {
661             int neww = (int) Math.ceil(w * 1.1);
662             int newh = (int) Math.ceil(h * 1.1);
663             return new Dimension(neww, newh);
664         }
665 
666         public void initContext(TestEnvironment env, GraphicsTests.Context ctx)
667         {
668             super.initContext(env, ctx);
669             ImageTests.Context ictx = (ImageTests.Context) ctx;
670 
671             ictx.tx = new AffineTransform();
672         }
673 
674         public void runTest(Object ctx, int numReps) {
675             ImageTests.Context ictx = (ImageTests.Context) ctx;
676             int x = ictx.initX;
677             int y = ictx.initY;
678             Graphics2D g = (Graphics2D) ictx.graphics;
679             g.translate(ictx.orgX, ictx.orgY);
680             Image src = ictx.src;
681             AffineTransform tx = ictx.tx;
682             if (ictx.animate) {
683                 if (ictx.touchSrc) {
684                     Graphics srcG = src.getGraphics();
685                     do {
686                         tx.setTransform(1.0, 0.1, 0.1, 1.0, x, y);
687                         srcG.fillRect(0, 0, 1, 1);
688                         g.drawImage(src, tx, null);
689                         if ((x -= 3) < 0) x += ictx.maxX;
690                         if ((y -= 1) < 0) y += ictx.maxY;
691                     } while (--numReps > 0);
692                 } else {
693                     do {
694                         tx.setTransform(1.0, 0.1, 0.1, 1.0, x, y);
695                         g.drawImage(src, tx, null);
696                         if ((x -= 3) < 0) x += ictx.maxX;
697                         if ((y -= 1) < 0) y += ictx.maxY;
698                     } while (--numReps > 0);
699                 }
700             } else {
701                 tx.setTransform(1.0, 0.1, 0.1, 1.0, x, y);
702                 if (ictx.touchSrc) {
703                     Graphics srcG = src.getGraphics();
704                     do {
705                         srcG.fillRect(0, 0, 1, 1);
706                         g.drawImage(src, tx, null);
707                     } while (--numReps > 0);
708                 } else {
709                     do {
710                         g.drawImage(src, tx, null);
711                     } while (--numReps > 0);
712                 }
713             }
714             g.translate(-ictx.orgX, -ictx.orgY);
715         }
716     }
717 
718     private static abstract class ImageOpTests extends ImageTests {
719         ImageOpTests(Group parent, String nodeName, String desc) {
720             super(parent, nodeName, desc,
721                   new Modifier.Filter() {
722                       public boolean isCompatible(Object val) {
723                           // Filter out all non-BufferedImage sources
724                           DrawableImage di = (DrawableImage) val;
725                           Group imgtype = di.getParent();
726                           return
727                               !(imgtype instanceof VolatileImg) &&
728                               !(imgtype instanceof OffScreen);
729                       }
730                   });
731             addDependencies(imageOpOptRoot, true);
732         }
733 
734         private static class Context extends ImageTests.Context {
735             BufferedImageOp bufImgOp;
736             BufferedImage   bufSrc;
737             BufferedImage   bufDst;
738 
739             RasterOp        rasterOp;
740             Raster          rasSrc;
741             WritableRaster  rasDst;
742         }
743 
744         public GraphicsTests.Context createContext() {
745             return new ImageOpTests.Context();
746         }
747 
748         public void initContext(TestEnvironment env,
749                                 GraphicsTests.Context ctx)
750         {
751             super.initContext(env, ctx);
752             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
753 
754             // Note: We filter out all non-BufferedImage sources in the
755             // ImageOpTests constructor above, so the following is safe...
756             ictx.bufSrc = (BufferedImage)ictx.src;
757 
758             String op = (String)env.getModifier(opList);
759             if (op.startsWith("convolve")) {
760                 Kernel kernel;
761                 if (op.startsWith("convolve3x3")) {
762                     // 3x3 blur
763                     float[] data = {
764                         0.1f, 0.1f, 0.1f,
765                         0.1f, 0.2f, 0.1f,
766                         0.1f, 0.1f, 0.1f,
767                     };
768                     kernel = new Kernel(3, 3, data);
769                 } else { // (op.startsWith("convolve5x5"))
770                     // 5x5 edge
771                     float[] data = {
772                         -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
773                         -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
774                         -1.0f, -1.0f, 24.0f, -1.0f, -1.0f,
775                         -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
776                         -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
777                     };
778                     kernel = new Kernel(5, 5, data);
779                 }
780                 int edge = op.endsWith("zero") ?
781                     ConvolveOp.EDGE_ZERO_FILL : ConvolveOp.EDGE_NO_OP;
782                 ictx.bufImgOp = new ConvolveOp(kernel, edge, null);
783             } else if (op.startsWith("lookup")) {
784                 if (op.endsWith("byte")) {
785                     byte invert[] = new byte[256];
786                     byte ordered[] = new byte[256];
787                     for (int j = 0; j < 256 ; j++) {
788                         invert[j] = (byte)(255-j);
789                         ordered[j] = (byte)j;
790                     }
791                     if (op.equals("lookup1byte")) {
792                         ictx.bufImgOp =
793                             new LookupOp(new ByteLookupTable(0, invert),
794                                          null);
795                     } else { // (op.equals("lookup3byte"))
796                         byte[][] yellowInvert =
797                             new byte[][] { invert, invert, ordered };
798                         ictx.bufImgOp =
799                             new LookupOp(new ByteLookupTable(0, yellowInvert),
800                                          null);
801                     }
802                 } else { // (op.endsWith("short"))
803                     short invert[] = new short[256];
804                     short ordered[] = new short[256];
805                     for (int j = 0; j < 256 ; j++) {
806                         invert[j] = (short)((255-j) * 255);
807                         ordered[j] = (short)(j * 255);
808                     }
809                     if (op.equals("lookup1short")) {
810                         ictx.bufImgOp =
811                             new LookupOp(new ShortLookupTable(0, invert),
812                                          null);
813                     } else { // (op.equals("lookup3short"))
814                         short[][] yellowInvert =
815                             new short[][] { invert, invert, ordered };
816                         ictx.bufImgOp =
817                             new LookupOp(new ShortLookupTable(0, yellowInvert),
818                                          null);
819                     }
820                 }
821             } else if (op.equals("rescale1band")) {
822                 ictx.bufImgOp = new RescaleOp(0.5f, 10.0f, null);
823             } else if (op.equals("rescale3band")) {
824                 float[] scaleFactors = { 0.5f,  0.3f, 0.8f };
825                 float[] offsets      = { 5.0f, -7.5f, 1.0f };
826                 ictx.bufImgOp = new RescaleOp(scaleFactors, offsets, null);
827             } else {
828                 throw new InternalError("Invalid image op");
829             }
830 
831             ictx.rasterOp = (RasterOp)ictx.bufImgOp;
832         }
833     }
834 
835     private static class DrawImageOp extends ImageOpTests {
836         DrawImageOp() {
837             super(graphicsTestRoot, "drawimageop",
838                   "drawImage(srcBufImg, op, x, y);");
839         }
840 
841         public void runTest(Object ctx, int numReps) {
842             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
843             int x = ictx.initX;
844             int y = ictx.initY;
845             BufferedImageOp op = ictx.bufImgOp;
846             BufferedImage src = ictx.bufSrc;
847             Graphics2D g2 = (Graphics2D)ictx.graphics;
848             g2.translate(ictx.orgX, ictx.orgY);
849             if (ictx.animate) {
850                 if (ictx.touchSrc) {
851                     Graphics gSrc = src.getGraphics();
852                     do {
853                         gSrc.fillRect(0, 0, 1, 1);
854                         g2.drawImage(src, op, x, y);
855                         if ((x -= 3) < 0) x += ictx.maxX;
856                         if ((y -= 1) < 0) y += ictx.maxY;
857                     } while (--numReps > 0);
858                 } else {
859                     do {
860                         g2.drawImage(src, op, x, y);
861                         if ((x -= 3) < 0) x += ictx.maxX;
862                         if ((y -= 1) < 0) y += ictx.maxY;
863                     } while (--numReps > 0);
864                 }
865             } else {
866                 if (ictx.touchSrc) {
867                     Graphics gSrc = src.getGraphics();
868                     do {
869                         gSrc.fillRect(0, 0, 1, 1);
870                         g2.drawImage(src, op, x, y);
871                     } while (--numReps > 0);
872                 } else {
873                     do {
874                         g2.drawImage(src, op, x, y);
875                     } while (--numReps > 0);
876                 }
877             }
878             g2.translate(-ictx.orgX, -ictx.orgY);
879         }
880     }
881 
882     private static class BufImgOpFilter extends ImageOpTests {
883         private boolean cached;
884 
885         BufImgOpFilter(boolean cached) {
886             super(bufImgOpTestRoot,
887                   "filter" + (cached ? "cached" : "null"),
888                   "op.filter(srcBufImg, " +
889                   (cached ? "cachedCompatibleDestImg" : "null") + ");");
890             this.cached = cached;
891         }
892 
893         public void initContext(TestEnvironment env,
894                                 GraphicsTests.Context ctx)
895         {
896             super.initContext(env, ctx);
897             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
898 
899             if (cached) {
900                 ictx.bufDst =
901                     ictx.bufImgOp.createCompatibleDestImage(ictx.bufSrc, null);
902             }
903         }
904 
905         public void runTest(Object ctx, int numReps) {
906             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
907             BufferedImageOp op = ictx.bufImgOp;
908             BufferedImage src = ictx.bufSrc;
909             BufferedImage dst = ictx.bufDst;
910             if (ictx.touchSrc) {
911                 Graphics gSrc = src.getGraphics();
912                 do {
913                     gSrc.fillRect(0, 0, 1, 1);
914                     op.filter(src, dst);
915                 } while (--numReps > 0);
916             } else {
917                 do {
918                     op.filter(src, dst);
919                 } while (--numReps > 0);
920             }
921         }
922     }
923 
924     private static class RasterOpFilter extends ImageOpTests {
925         private boolean cached;
926 
927         RasterOpFilter(boolean cached) {
928             super(rasterOpTestRoot,
929                   "filter" + (cached ? "cached" : "null"),
930                   "op.filter(srcRaster, " +
931                   (cached ? "cachedCompatibleDestRaster" : "null") + ");");
932             this.cached = cached;
933         }
934 
935         public void initContext(TestEnvironment env,
936                                 GraphicsTests.Context ctx)
937         {
938             super.initContext(env, ctx);
939             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
940 
941             ictx.rasSrc = ictx.bufSrc.getRaster();
942             if (cached) {
943                 ictx.bufDst =
944                     ictx.bufImgOp.createCompatibleDestImage(ictx.bufSrc, null);
945                 ictx.rasDst = ictx.bufDst.getRaster();
946             }
947         }
948 
949         public void runTest(Object ctx, int numReps) {
950             ImageOpTests.Context ictx = (ImageOpTests.Context)ctx;
951             RasterOp op = ictx.rasterOp;
952             Raster src = ictx.rasSrc;
953             WritableRaster dst = ictx.rasDst;
954             if (ictx.touchSrc) {
955                 Graphics gSrc = ictx.bufSrc.getGraphics();
956                 do {
957                     gSrc.fillRect(0, 0, 1, 1);
958                     op.filter(src, dst);
959                 } while (--numReps > 0);
960             } else {
961                 do {
962                     op.filter(src, dst);
963                 } while (--numReps > 0);
964             }
965         }
966     }
967 }